using System;
using System.Diagnostics;

using org.fourzoas.noise.module;

namespace org.fourzoas.noise.module {
  /// <summary>
  /// Noise module that outputs a weighted blend of the output values from two source modules given
  /// the output value supplied by a control module.
  /// </summary>
  /// <remarks>
  /// <img src="file://C:/temp/noise/noise/doc/html/moduleblend.png"/>
  /// <para>
  /// Unlike most other noise modules, the index value assigned to a source module determines its role
  /// in the blending operation:
  /// <list type="bullet">
  /// <item>Source module 0 (upper left in the diagram) outputs one of the values to blend.</item>
  /// <item>Source module 1 (lower left in the diagram) outputs one of the values to blend.</item>
  /// <item>Source module 0 (bottom of the diagram) is known as the <em>control module</em>.
  /// The control module determines the weight of the blending operation.  Negative values weigh the blend
  /// towards the output value from the source module with an index value of 0.  Positive values weigh the 
  /// blend towards the output value from the source module with an index value of 1.</item>
  /// </list>
  /// </para>
  /// <para>
  /// An application can pass the control module to the <see cref="ControlModule"/> property instead of the 
  /// <see cref="Module.SourceModules"/> property.  This may make the application code easier to read.
  /// </para>
  /// <para>
  /// This noise module uses linear interpolation to perform the blending operation.
  /// </para>
  /// <para>This noise module requires three source modules.</para>
  /// </remarks>
  public class Blend : SelectorModule {
    /// <summary>
    /// Default constructor.
    /// </summary>
    public Blend()
      : base(3) {
    }

    /// <summary>
    /// Sets or returns the control module.
    /// </summary>
    /// <exception cref="NoModuleException">See the preconditions for more information.</exception>
    /// <remarks>
    /// <para>The control module determines the weight of the blending
    /// operation.  Negative values weigh the blend towards the output
    /// value from the source module with an index value of 0.  Positive
    /// values weigh the blend towards the output value from the source
    /// module with an index value of 1.</para>
    /// <h5>Preconditions</h5>
    /// <para>A control module has been added to this noise module via either <see cref="Module.SourceModules"/> or
    /// <see cref="ControlModule"/>.</para>
    /// </remarks>
    public Module ControlModule {
      get {
        if (m_sourceModule == null || m_sourceModule.Count < 3 || m_sourceModule[2] == null)
          throw new NoModuleException();
        return m_sourceModule[2];
      }
      set {
        Debug.Assert(m_sourceModule != null);
        m_sourceModule[2] = value;
      }
    }

    /// <summary>
    /// Generates an output value given the coordinates of the specified input value.
    /// </summary>
    /// <param name="x">The <em>x</em> coordinate of the input value.</param>
    /// <param name="y">The <em>y</em> coordinate of the input value.</param>
    /// <param name="z">The <em>z</em> coordinate of the input value.</param>
    /// <returns>The output value.</returns>
    public override double GetValue(double x, double y, double z) {
      Debug.Assert(m_sourceModule[0] != null);
      Debug.Assert(m_sourceModule[1] != null);
      Debug.Assert(m_sourceModule[2] != null);

      double v0 = m_sourceModule[0].GetValue(x, y, z);
      double v1 = m_sourceModule[1].GetValue(x, y, z);
      double alpha = (m_sourceModule[2].GetValue(x, y, z) + 1.0) / 2.0;
      return Globals.LinearInterp(v0, v1, alpha);
    }

    /// <summary>
    /// Returns the number of source modules required by this noise module.
    /// </summary>
    /// <returns>The number of source modules required by this noise module.</returns>
    public override int GetSourceModuleCount() {
      return 3;
    }
  }
}
